之前在談到無論是消息佇列或分散式交易時,我們都有提到Redis的持久化不可靠,但我們只是簡短的敘述,並沒有解釋有多麽不可靠。
因此,這篇文章我們將從Redis的實作來告訴你,Redis的持久化到底怎麼運作的。
但在開始前,我們先上結論。
Redis無法保證資料不會遺失,即便使用最嚴格的設定也一樣。
但為了避免大家誤會,這篇文章指的Redis都是指單一一台Redis或者Redis的主從複製,並不包含Redis叢集。
最嚴格的設定則是指開啟Redis的AOF
功能,並且在每次指令結束都做fsync
。也許有些人對AOF
感到陌生,因此我先進一步解釋AOF
的原理。
AOF (Append Only File)是一個類似LSM樹的機制。
當Redis處理指令結束後,Redis會將這個指令以某種形式給存進log檔,而且不會做任何修改,就是直接寫在檔案末尾。
但這個檔案並沒有實際寫進硬碟,而是保留在記憶體中,因此若是Redis直接掛掉,這個AOF
依然會遺失。
在泛Unix系統上,要將檔案寫進硬碟使用的是fsync
這個系統指令,至於寫進硬碟的時機,在Redis則有三個可以設定的選項。
fsync
fsync
一次fsync
效能最好的選項是第一個,接著會越來越糟,從這個實驗我們可以看到,第三個選項的效能遠輸LSM樹。
根據官方文件描述,當AOF開啟後且使用第三個選項,資料能夠被妥善保存。但,大錯特錯。
問題在於AOF
的發動時機,如果更仔細看Redis的實作,不難發現AOF
的發動時機是在處理完指令「之後」。這做法不像是大部分資料庫的WAL (Write Ahead Log)實作,WAL的做法是當在處理指令時同時也在寫入log。
因此,在Redis執行完指令卻馬上掛掉,那麼還來不及執行的AOF
也不會生效,修改就遺失了。
fsync
一次每次結束指令都做fsync
會嚴重影響效能,因此通常情況,我們僅會採用每秒fsync
一次,這同時也是Redis開啟AOF
後的預設選項。
那麼,問題來了。
我們真的只會遺失1秒內資料嗎?
答案是,錯。
在AOF
寫檔案時有兩個步驟:
fsync
,也就是實際把檔案寫進硬碟根據Redis的實作,我把流程圖畫出來如下:
總結一下,如果Redis掛掉,那麼我們會損失「2秒」內的資料。
另一方面,官網描述的是:
appendfsync everysec: fsync every second. Fast enough (in 2.4 likely to be as fast as snapshotting), and you can lose 1 second of data if there is a disaster.
這是不正確的。
使用Redis的AOF
的確可以提升資料持久化的可靠度,但是不可能做到完全沒有資料遺失。
若是希望盡可能地保存資料,那麼就必須要將AOF
開啟並開啟RDB
(資料快照)。
當然,若是能力允許,應該要使用Redis叢集,這才是最可靠的方式。但一個完整的叢集會需要管理非常多Redis實體,包含三個完整的主本、副本群組和哨兵群組,無論是管理或成本都比單一一個Redis大上不少。